package edu.umd.essic.maskbuilder;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.swing.JOptionPane;
import java.awt.*;

public class Utils {

    public final static String gmb = "gmb";
    public final static String dat = "dat";
    public final static String dym = "dym";
    public final static String txt = "txt";
    public final static String g98 = "g98";
    public final static String bin = "bin";
    public final static String par = "par";
    private static Color[] palette;
    private static int ncols = 1024;  
    public static final int NBytes = 4;

     /****************************
     * Get the extension of a file.
     ****************************/
    public static String getExtension(File f) {
        String ext = null;
        String s = f.getName();
        int i = s.lastIndexOf('.');

        if (i > 0 &&  i < s.length() - 1) {
            ext = s.substring(i+1).toLowerCase();
        }
        return ext;
    }
   
    public static Color[] GetColors(){

        int max = 255;
        palette = new Color[ncols];
        for (int i=0; i<64; i++){
                palette[i] = new Color(150-i,0,255);
            }
        for (int i=0; i<60; i++){
            for (int j=0; j<16; j++){
                int k = 64+j+16*i;
                if (i<=15)
                    palette[k] = new Color(0,j+16*i,225);
                if ((i>15) && (i<=30))
                    palette[k] = new Color(0,255,255-(j+16*(i-15)));
                if ((i>30) && (i<=45))
                    palette[k] = new Color(j+16*(i-30),255,0);
                if (i>45)
                    palette[k] = new Color(255,255-(j+16*(i-45)),0);                
            }
        }
        return palette;
    }
     /****************************
     * writes integer variable to binary file, 
     * in little_endian format.
     ****************************/
    public static void writeInt(OutputStream out, int value) throws IOException {
        byte byte0 = (byte) ( (value & 0xff000000) >> 24);
        byte byte1 = (byte) ( (value & 0xff0000) >> 16);
        byte byte2 = (byte) ( (value & 0xff00) >> 8);
        byte byte3 = (byte) ( (value & 0xff) >> 0);
        byte[] buffer = new byte[NBytes];
        buffer[0] = byte3;
        buffer[1] = byte2;
        buffer[2] = byte1;
        buffer[3] = byte0;
        out.write(buffer);
    }

    /****************************
     * writes character to binary file, 
     * in little_endian format.
     ****************************/
    public static void writeChar(OutputStream out, char[] chars) throws IOException {
        byte[] buf = new byte[chars.length];
        buf[0] = (byte) chars[0];
        buf[1] = (byte) chars[1];
        buf[2] = (byte) chars[2];
        buf[3] = (byte) chars[3];
        out.write(buf);
    }

     /****************************
     * writes array of integer variable to  
     * binary file in little_endian format.
     ****************************/
    public static void writeInt(OutputStream out, int[] values) throws IOException {
        byte[] buffer = new byte[values.length * NBytes];
        for (int i = 0; i < values.length; i++) {
            int value = values[i];
            byte byte0 = (byte) ( (value & 0xff000000) >> 24);
            byte byte1 = (byte) ( (value & 0xff0000) >> 16);
            byte byte2 = (byte) ( (value & 0xff00) >> 8);
            byte byte3 = (byte) ( (value & 0xff) >> 0);
            buffer[i * NBytes + 0] = byte3;
            buffer[i * NBytes + 1] = byte2;
            buffer[i * NBytes + 2] = byte1;
            buffer[i * NBytes + 3] = byte0;
        }
        out.write(buffer);
    }

     /****************************
     * writes float variable to binary file, 
     * in little_endian format.
     ****************************/
    public static void writeFloat(OutputStream out, float value) throws IOException {
      int intValue = Float.floatToIntBits(value);
      writeInt(out,intValue);
   }
 
     /****************************
     * writes array of float variable to  
     * binary file in little_endian format.
     ****************************/
    public static void writeFloat(OutputStream out, float[] values) throws IOException {
        int[] intValues = new int[values.length];
        for (int i = 0; i < values.length; i++) {
            intValues[i] = Float.floatToIntBits(values[i]);
        }
        writeInt(out,intValues);
    }
    
    public static void writeFloat(OutputStream out, float[] values, int[] indexes, int nrow) throws IOException {
        int length = values.length+indexes.length;
        int[] intValues = new int[length];
        for (int i = 0; i < indexes.length/2; i++) {
            intValues[i*nrow]   = indexes[i*2];
            intValues[i*nrow+1] = indexes[i*2+1];
            for (int k=2; k<nrow; k++)
                intValues[i*nrow+k] = Float.floatToIntBits(values[i*(nrow-2)+k-2]);
        }
        writeInt(out,intValues);
    }

     /****************************
     * writes long variable to binary file, 
     * in little_endian format.
     ****************************/
    public static void writeLong(OutputStream out, long value) throws IOException {
        byte byte0 = (byte) ( (value & 0xff) >> 56);
        byte byte1 = (byte) ( (value & 0xff) >> 48);
        byte byte2 = (byte) ( (value & 0xff) >> 40);
        byte byte3 = (byte) ( (value & 0xff) >> 32);
        byte byte4 = (byte) ( (value & 0xff) >> 24);
        byte byte5 = (byte) ( (value & 0xff) >> 16);
        byte byte6 = (byte) ( (value & 0xff) >> 8);
        byte byte7 = (byte) ( (value & 0xff) >> 0);
        byte[] buffer = new byte[8];
        buffer[0] = byte7;
        buffer[1] = byte6;
        buffer[2] = byte5;
        buffer[3] = byte4;
        buffer[4] = byte3;
        buffer[5] = byte2;
        buffer[6] = byte1;
        buffer[7] = byte0;
        out.write(buffer);
    }
    
         /****************************
     * writes array of integer variable to  
     * binary file in little_endian format.
     ****************************/
    public static void writeLong(OutputStream out, long[] values) throws IOException {
        byte[] buffer = new byte[values.length * 8];
        for (int i = 0; i < values.length; i++) {
            long value = values[i];
            byte byte0 = (byte) ( (value & 0xff) >> 56);
            byte byte1 = (byte) ( (value & 0xff) >> 48);
            byte byte2 = (byte) ( (value & 0xff) >> 40);
            byte byte3 = (byte) ( (value & 0xff) >> 32);
            byte byte4 = (byte) ( (value & 0xff) >> 24);
            byte byte5 = (byte) ( (value & 0xff) >> 16);
            byte byte6 = (byte) ( (value & 0xff) >> 8);
            byte byte7 = (byte) ( (value & 0xff) >> 0);
            buffer[i * 8 + 0] = byte7;
            buffer[i * 8 + 1] = byte6;
            buffer[i * 8 + 2] = byte5;
            buffer[i * 8 + 3] = byte4;
            buffer[i * 8 + 4] = byte3;
            buffer[i * 8 + 5] = byte2;
            buffer[i * 8 + 6] = byte1;
            buffer[i * 8 + 7] = byte0;
        }
        out.write(buffer);
    }
 
   public static void writeDouble(OutputStream out, double value) throws IOException {
      long longValue = Double.doubleToLongBits(value);
      writeLong(out,longValue);
   }
 
     /****************************
     * writes array of float variable to  
     * binary file in little_endian format.
     ****************************/
    public static void writeDouble(OutputStream out, double[] values) throws IOException {
        long[] longValues = new long[values.length];
        for (int i = 0; i < values.length; i++) {
            longValues[i] = Double.doubleToLongBits(values[i]);
        }
        writeLong(out,longValues);
    }
    
    public static double getMIN(double[][][] value, int nx, int ny, int nn){
        double min = 1e6f;
        for (int n=0; n<nn; n++){
            for (int i=0; i<nx; i++){
                for (int j=0; j<ny; j++){
                    if (value[i][j][n] < min)
                        min = value[i][j][n];
                }
            }
        }
        return min;
    }
    
    public static double getMAX(double[][][] value, int nx, int ny, int nn){
        double max = 0;
        for (int n=0; n<nn; n++){
            for (int i=0; i<nx; i++){
                for (int j=0; j<ny; j++){
                    if (value[i][j][n] > max)
                        max = value[i][j][n];
                }
            }
        }
        return max;
    }
    
    public static double getMIN(double[][] value, int nx, int ny, int[][] mask){
        double min = 1e6;
            for (int i=0; i<nx; i++){
                for (int j=0; j<ny; j++){
                    if (mask[i][j]!=0){
                        if (value[i][j] < min)
                            min = value[i][j];
                    }
                }
            }
        return min;
    }
       
    public static double getMAX(double[][] value, int nx, int ny, int[][] mask){
        double max = 0;
            for (int i=0; i<nx; i++){
                for (int j=0; j<ny; j++){
                    if (mask[i][j]!=0){
                        if (value[i][j] > max)
                            max = value[i][j];
                    }
                }
            }
        return max;
    }
        
    public static float getMIN(float[][] value, int nx, int ny, int[][] mask){
        float min = (float)1e9;
            for (int i=0; i<nx; i++){
                for (int j=0; j<ny; j++){
                    if (mask[i][j]!=0){
                        if (value[i][j] < min)
                            min = value[i][j];
                    }
                }
            }
        return min;
    }
    public static float getMAX(float[][] value, int nx, int ny, int[][] mask){
        float max = (float)-1e9;
            for (int i=0; i<nx; i++){
                for (int j=0; j<ny; j++){
                    if (mask[i][j]!=0){
                        if (value[i][j] > max)
                            max = value[i][j];
                    }
                }
            }
        return max;
    }
    
    public static int getMIN(int[][] value, int nx, int ny){
        int min = 100000000;
            for (int i=0; i<nx; i++){
                for (int j=0; j<ny; j++){
                    if (value[i][j] < min)
                        min = value[i][j];
                }
            }
        return min;
    }
       
    public static int getMAX(int[][] value, int nx, int ny){
        int max = 0;
            for (int i=0; i<nx; i++){
                for (int j=0; j<ny; j++){
                    if (value[i][j] > max)
                        max = value[i][j];
                }
            }
        return max;
    }
    
    public static float getMIN(float[][] value, int nx, int ny){
        float min = 100000000;
            for (int i=0; i<nx; i++){
                for (int j=0; j<ny; j++){
                    if ((value[i][j]!=0)&&(value[i][j] < min))
                        min = value[i][j];
                }
            }
        return min;
    }
       
    public static float getMAX(float[][] value, int nx, int ny){
        float max = 0;
            for (int i=0; i<nx; i++){
                for (int j=0; j<ny; j++){
                    if ((value[i][j]!=0)&&(value[i][j] > max))
                        max = value[i][j];
                }
            }
        return max;
    }
    
    public static void Initialize(float[][] var, int nx, int ny){
        for (int i = 0; i < nx; i++) {
            for (int j = 0; j < ny; j++) {
                var[i][j] = 0.f;
            }
        }
    }

    public static void Initialize(double[][] var, int nx, int ny){
        for (int i = 0; i < nx; i++) {
            for (int j = 0; j < ny; j++) {
                var[i][j] = 0.0;
            }
        }
    }

     /****************************
     * reads integer variable from binary file, 
     * as a little_endian number.
     ****************************/
    public static int readInt(DataInputStream in) throws IOException {
        int bits = 0;  
        for (int ii = 0; ii < 32; ii += 8)
             bits += (int)in.read() << ii;
        return bits;
    }
    
    public static void FileWriteError(String name){
        JOptionPane.showMessageDialog(null,"Failed to write the file " + name +
        ". Program will be closed.", "File writing error",JOptionPane.ERROR_MESSAGE);  
        System.exit(0);
    }
    
    public static void FileReadError(String name){
        JOptionPane.showMessageDialog(null,"Failed to read the file " + name +
        ". Program will be closed.", "File reading error",JOptionPane.ERROR_MESSAGE);  
        System.exit(0);
    }
    
    //=========== old stuff =====================
       /****************************
     * reads float variable from binary file 
     * as a little_endian number.
     ****************************/
    public static float readFloat(DataInputStream in) throws IOException {
        int bits = 0;
        for (int ii = 0; ii < 32; ii += 8)
             bits += (int)in.read() << ii;
        return Float.intBitsToFloat(bits);
    }

    private byte[] IntToBuffer(int var){
        ByteBuffer buf = ByteBuffer.allocate(4);
        buf.clear();
        buf.order(ByteOrder.LITTLE_ENDIAN);
        buf.putInt(var);
        return buf.array();
    }
    
    private byte[] FloatToBuffer(float var){
        ByteBuffer buf = ByteBuffer.allocate(4);
        buf.clear();
        buf.order(ByteOrder.LITTLE_ENDIAN); 
        buf.putFloat(var);
        return buf.array();
    }
    
      /*
       *old reading routines*
       ReadDymeFileHeader(); 
       int i,j,k;
        try{
            FileInputStream fileInput = new FileInputStream(inputbin); 
            DataInputStream in    = new DataInputStream(fileInput);

            nlon      = Utils.readInt(in);
            nlat      = Utils.readInt(in);
            nlevel    = Utils.readInt(in);
            startdate = Utils.readFloat(in);
            enddate   = Utils.readFloat(in);
            
            zlevel = new float[nlevel];
            for (j=0;j<nlon;j++)
            {
                xlon[j]   = Utils.readFloat(in);
            }
            for (i=0;i<nlat;i++)
            {
                ylat[i]   = Utils.readFloat(in);
            }
            for (k=0;k<nlevel;k++)
            {
                zlevel[k] = Utils.readFloat(in);
            }
            enddate = zlevel[nlevel-1];
            // there is no necessity to read mask data 
            in.close();
        }catch (IOException e) {
            System.out.println("Failed task...");
            System.exit(0);
        }	
       ReadDymFile(){
       int i,j;
       try {
            FileInputStream ifile = new FileInputStream(inputbin);
            DataInputStream in    = new DataInputStream(ifile); 
            in.skip(nbytetoskip);
            for (i=0;i<nlon;i++){
                    for (j=0;j<nlat;j++){
                         mat2d[i][j]= 0.f;
                    }
            }
            for (j=0;j<nlat;j++){
                    for (i=0;i<nlon;i++){
                        mat2d[i][j] = Utils.readFloat(in); 
                    }		
            }	
            in.close();
        }   catch (IOException e) {
            System.out.println("Dym File Reading Error:" + e);    
            System.exit(0);
            }
        */
     /* old writing routine*
     /*ByteBuffer dim_buffer = null;
        dim_buffer = ByteBuffer.allocate(4);
        dim_buffer.order(ByteOrder.LITTLE_ENDIAN);
        FileChannel fc = out.getChannel();
        for (int j=0;j<nlat;j++)
            for (int i=0;i<nlon;i++){
                dim_buffer.putFloat(vfin[i][j]);
                fc.write(dim_buffer);
                dim_buffer.clear();
            }*/
    //==============================================
  /*
       private Point[] FindCorners(Point[] cen_p, int n, float reg_dx, float reg_dy){
        Point[] indexes = new Point[4];
        for(int j=cen_p[n].y; j>0; j--){
            for(int i= cen_p[n].x; i>0;i--){
                float dx = (float)(coord[i][j].x-coord[i-1][j].x);
                float dy = (float)(coord[i][j].y-coord[i][j-1].y);
                if ((dx == reg_dx)&&(dy == reg_dy)){
                    indexes[0].x = i;//ul corner
                    indexes[0].y = j;
                    break;
                }
            }
        }  
        for(int j=cen_p[n].y; j>0; j--){
            for(int i= cen_p[n].x; i<size_x-1;i++){
                float dx = (float)(coord[i+1][j].x-coord[i][j].x);
                float dy = (float)(coord[i][j].y-coord[i][j-1].y);
                if ((dx == reg_dx)&&(dy == reg_dy)){
                    indexes[1].x = i; //ur corner
                    indexes[1].y = j;
                    break;
                }
            }
        }  
        for(int j=cen_p[n].y; j<size_y-1; j++){
            for(int i= cen_p[n].x; i>0;i--){
                float dx = (float)(coord[i][j].x-coord[i-1][j].x);
                float dy = (float)(coord[i][j+1].y-coord[i][j].y);
                if ((dx == reg_dx)&&(dy == reg_dy)){
                    indexes[2].x = i; //ll corner
                    indexes[2].y = j;
                    break;                
                }
            }
        }  
        for(int j=cen_p[n].y; j<size_y-1; j++){
            for(int i= cen_p[n].x; i<size_x-1;i++){
                float dx = (float)(coord[i+1][j].x-coord[i][j].x);
                float dy = (float)(coord[i][j+1].y-coord[i][j].y);
                if ((dx == reg_dx)&&(dy == reg_dy)){
                    indexes[3].x = i; //lr corner
                    indexes[3].y = j;
                    break;                
                }
            }
        }  
        return indexes;
    }
*/ 
   
}
